/**
 * 
 */
package com.code.google.p.jyve.database;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


/** 
 * @author Boudewijn
 */
class Database {
	
	// <stof {zuur="[naam]" kb="[getal]"} {base="[naam]" kz="[getal]"} naam="[naam]" />
	
	private static final String FILENAME = "stoffen.xml";
	
	private static final String ZUUR_ATTR = "zuur";
	private static final String KB_ATTR = "kb";
	private static final String BASE_ATTR = "base";
	private static final String KZ_ATTR = "kz";
	private static final String NAAM_ATTR = "naam";
	private static final String ROOT_EL = "root";
	private static final String STOF_EL = "stof";
	
	private DocumentBuilder mBuilder;
	private HashMap<String, Stof> mStoffen = new HashMap<String, Stof>();
	private HashMap<String, Node> mStofNodes = new HashMap<String, Node>();
	
	
	public Database(){
		System.err.println("Opening database");
		File xmlFile = new File(FILENAME);
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			mBuilder = factory.newDocumentBuilder();				

			if (xmlFile.exists()) {
				Document doc = mBuilder.parse(xmlFile);
				
				
				doc.getDocumentElement().normalize();

				Node root = doc.getFirstChild();
				NodeList stoffenNodes = root.getChildNodes();
				
				for (int i = 0; i < stoffenNodes.getLength(); i++){
					Node node = stoffenNodes.item(i);
					if (node instanceof Element){
						String naam = node.getAttributes().getNamedItem(NAAM_ATTR).getNodeValue();
						mStofNodes.put(naam, node);
					}
				}
				
				for (int i = 0; i < stoffenNodes.getLength(); i++){
					try {
					Node node = stoffenNodes.item(i);
					if (node instanceof Element) createStof(node);
					}
					catch (Exception e){
						System.out.println("Fout in stoffen.xml, we gaan toch door. " +
								"In de error stream is de stack trace te vinden.");
						e.printStackTrace();
					}
				}
				
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally {
			mStofNodes = null; // RAM vrijmaken
		}
	}


	private Stof createStof(Node node) {
		NamedNodeMap attrs = node.getAttributes();
		String naam = attrs.getNamedItem(NAAM_ATTR).getNodeValue();
		Node zuur = attrs.getNamedItem(ZUUR_ATTR);
		Stof.Builder stofBuilder = new Stof.Builder(naam);
		Stof stof = stofBuilder.build();
		mStoffen.put(stof.getNaam(), stof);
		
		if (zuur != null){
			double kb = Double.parseDouble(attrs.getNamedItem(KB_ATTR).getNodeValue());
			stof.setZuur(createStof(zuur.getNodeValue()), kb);
		}
		
		Node base = attrs.getNamedItem(BASE_ATTR);
		
		if (base != null){
			double kz = Double.parseDouble(attrs.getNamedItem(KZ_ATTR).getNodeValue());
			stof.setBase(createStof(base.getNodeValue()), kz);
		}
		return stof;
	}
	
	private Stof createStof(String naam) {
		Stof stof = mStoffen.get(naam);
		if (stof != null) return stof;
		return createStof(mStofNodes.get(naam));
	}


	/**
	 * Vraagt een stof op.
	 * @param stof De naam van de stof. Conform de notatie stoffen.
	 * @return De opgevraagde stof of null als hij niet gevonden is.
	 */
	public Stof getStof(String stof){
		return mStoffen.get(stof);
	}

	public Stof getStof(int index){
		return mStoffen.values().toArray(new Stof[0])[index];
	}

	public int getAmount(){
		return mStoffen.size();
	}


	/**
	 * Voegt een stof aan de database toe.
	 * @param stof De stof die je toe wil voegen.
	 * @return Wel of niet gelukt, afhankelijk van aanwezigheid van stoffen met dezelfde naam.
	 */
	public boolean addStof(Stof stof){
		for (Stof i : mStoffen.values()){
			if (stof.getNaam().contentEquals(i.getNaam())){
				return false;
			}
		}
		mStoffen.put(stof.getNaam(), stof);
		Stof temp = stof;
		while ((temp = temp.verbaas()) != null){
			addStof(temp);
		}
		temp = stof;
		while ((temp = temp.verzuur()) != null){
			addStof(temp);
		}
		return true;
	}
	
	public void close(){
		File xmlFile = new File(FILENAME);
		Element root = null;
		
		if (xmlFile.exists()){
			xmlFile.delete();
		}
		
			
		Document doc = mBuilder.newDocument();
		root = doc.createElement(ROOT_EL);

		doc.appendChild(root);
		
		for (Stof i : mStoffen.values()){
			Element el = doc.createElement(STOF_EL);
			
			el.setAttribute(NAAM_ATTR, i.getNaam());
			
			if (i.isZuur()){
				Stof base = i.verbaas();
				el.setAttribute(BASE_ATTR, base.getNaam());
				
				el.setAttribute(KZ_ATTR, String.valueOf(i.getKz()));
			}
			if (i.isBase()){
				Stof zuur = i.verzuur();
				el.setAttribute(ZUUR_ATTR, zuur.getNaam());
				

				el.setAttribute(KB_ATTR, String.valueOf(i.getKb()));
			}
			
			root.appendChild(el);
		}
		try{
			TransformerFactory transformerFactory = TransformerFactory.newInstance();
			Transformer transformer = transformerFactory.newTransformer();
			DOMSource source = new DOMSource(doc);
			StreamResult result = new StreamResult(xmlFile);
	
	
			
			transformer.transform(source, result);
			
		
		}
		catch (Exception e){
			e.printStackTrace();
		}
		
	}

	public Stof[] getStoffen() {
		return mStoffen.values().toArray(new Stof[0]);
	}
}
